﻿using ERP.Workflow.Designer.Application.Contracts;
using ERP.Workflow.Designer.Domain;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.ObjectExtending;
using ERP.Workflow.Designer.Domain.Shared;
using System;
using ERP.Workflow.Designer.Domain.IRepository;
using Microsoft.AspNetCore.Authorization;

namespace ERP.Workflow.Designer.Application
{
    public class DefinitionAppService : WorkflowDesignerAppServiceBase, IDefinitionAppService
    {
        protected DefinitionManager _definitionManager;
       // protected readonly IRepository<Definition> _definitionRepository;
      //  protected readonly IRepository<Link> _linkRepository;


        protected readonly IDefinitionRepository _definitionRepository;
        protected readonly ILinkRepository _linkRepository;

        public DefinitionAppService(DefinitionManager definitionManager, IDefinitionRepository definitionRepository, ILinkRepository linkRepository)
        {
            _definitionManager = definitionManager;
            _definitionRepository = definitionRepository;
            _linkRepository = linkRepository;
        }

        public async Task<DefinitionDto> CreateAsync(DefinitionCreateDto input)
        {
            var entity = new Definition
            {
                Name = input.Name,
                Version = input.Version,
                TenantId = CurrentTenant.Id
            };
            input.MapExtraPropertiesTo(entity);
            await _definitionRepository.InsertAsync(entity);
            return ObjectMapper.Map<Definition, DefinitionDto>(entity);
        }
        public async Task DeleteAsync(Guid id)
        {
            await _definitionRepository.DeleteAsync(id);
        }
        public async Task<DefinitionDto> GetAsync(Guid id)
        {
            var entity = await _definitionManager.FindAsync(id, false);
            var rs = ObjectMapper.Map<Definition, DefinitionDto>(entity);
            return rs;
        }

        [Authorize(WorkflowDesignerPermissions.Designer)]
        public async Task<PagedResultDto<DefinitionDto>>? GetListAsync(GetDefinitionInput input)
        {
            var JJ = CurrentUser.Name;
            var bb = CurrentTenant.Name;
            var query = await _definitionRepository.GetQueryableAsync();
            query = query.WhereIf(string.IsNullOrEmpty(input.Filter), m => m.Name!.Contains(input.Filter ?? ""))
                .PageBy(input);
            var list = ObjectMapper.Map<List<Definition>, List<DefinitionDto>>(query.ToList());
            var count = query.Count();
            return new PagedResultDto<DefinitionDto> { Items = list, TotalCount = count };
        }

        public async Task<DefinitionSaveDto> SaveAsync(DefinitionSaveDto input)
        {
            try
            {
                if (!await IsExistAsync(input!.Definition.Id))
                {
                    var definition = ObjectMapper.Map<DefinitionSaveDto, Definition>(input);
                    await _definitionManager.CreateAsync(definition);

                }
                else
                {
                    var definition = await _definitionManager.FindAsync(input.Definition.Id, true);

                    NodeSave(input, definition);

                    PortSave(input, definition);
                    LinkSave(input, definition);
                    LabelSave(input, definition);
                    definition.Set(input.Definition.Name, input.Definition.Version);
                    _definitionRepository?.UpdateAsync(definition);
                }
                await CurrentUnitOfWork.SaveChangesAsync();
            }
            catch (Exception ex)
            {

            }
            return input;
        }
        private void LabelSave(DefinitionSaveDto input, Definition definition)
        {
            var dbids = definition.Labels.Select(m => m.Id);
            var nList = input.Links.SelectMany(m => m.Labels);
            var nids = nList.Select(m => m.Id);

            var dels = dbids.Except(nids);
            var adds = nids.Except(dbids);
            var edits = dbids.Intersect(nids);
            //删除
            definition.Labels.Where(m => dels.Contains(m.Id)).ToList().ForEach(m => m.SoftDelete());
            //更新
            foreach (var item in definition.Labels.Where(m => edits.Contains(m.Id)))
            {
                var n = nList.First(m => m.Id == item.Id);
                item.Set(n.ParentId, n.Content, n.Distance);
                if (n.OffsetX.HasValue && n.OffsetY.HasValue)
                    item.SetOffset(n.OffsetX.Value, n.OffsetY.Value);
                else
                    item.SetOffset(0, 0);
            }
            //添加
            foreach (var n in nList.Where(m => adds.Contains(m.Id)))
            {
                var l = new LinkLabel();
                l.SetId(l.Id);
                l.Set(n.ParentId, n.Content, n.Distance);
                if (n.OffsetX.HasValue && n.OffsetY.HasValue)
                    l.SetOffset(n.OffsetX.Value, n.OffsetY.Value);
                else
                    l.SetOffset(0, 0);
                definition.Labels.Add(l);
            }

        }
        private void LinkSave(DefinitionSaveDto input, Definition definition)
        {
            var dbids = definition.Links.Select(m => m.Id);
            var nList = input.Links;
            var nids = nList.Select(m => m.Id);

            var dels = dbids.Except(nids);
            var adds = nids.Except(dbids);
            var edits = dbids.Intersect(nids);
            //删除线
            definition.Links.Where(m => dels.Contains(m.Id)).ToList().ForEach(m => m.SoftDelete()); ;

            //更新

            foreach (var item in definition.Links.Where(m => edits.Contains(m.Id)))
            {
                var n = nList.First(m => m.Id == item.Id);
                item.Set(n!.Content, n.Paths, n.Width, n.Color, n.SelectedColor);
                item.SetTargetLinkMarker(n.TargetLinkMarkerPath, n.TargetLinkMarkerWidth);
                item.SetSourceLinkMarker(n.SourceLinkMarkerPath, n.SourceLinkMarkerWidth);
                item.SetTargetNodeId(n.TargetNodeId);
                item.SetSourceNodeId(n.SourceNodeId);
                item.SetSourcePortId(n.SourcePortId);
                item.SetTargetPortId(n.TargetPortId);
                n.ExtraProperties.ExtraPropertyDataToObject<Link>(item);
            }
            //添加
            foreach (var n in nList.Where(m => adds.Contains(m.Id)))
            {
                var item = new Link();
                item.SetId(n.Id);
                item.Set(n!.Content, n.Paths, n.Width, n.Color, n.SelectedColor);
                item.SetTargetLinkMarker(n.TargetLinkMarkerPath, n.TargetLinkMarkerWidth);
                item.SetSourceLinkMarker(n.SourceLinkMarkerPath, n.SourceLinkMarkerWidth);
                item.SetTargetNodeId(n.TargetNodeId);
                item.SetSourceNodeId(n.SourceNodeId);
                item.SetSourcePortId(n.SourcePortId);
                item.SetTargetPortId(n.TargetPortId);
                n.ExtraProperties.ExtraPropertyDataToObject<Link>(item);
                definition.Links.Add(item);
            }
        }
        private void PortSave(DefinitionSaveDto input, Definition definition)
        {
            var dbids = definition.Ports.Select(m => m.Id);
            var nList = input.Nodes.SelectMany(m => m.Ports);
            var nids = nList.Select(m => m.Id);

            var dels = dbids.Except(nids).ToList();
            var adds = nids.Except(dbids).ToList();
            var edits = dbids.Intersect(nids).ToList();
            //删除
            definition.Ports.Where(m => dels.Contains(m.Id)).ToList().ForEach(m => m.SorftDelet());
            //更新
            foreach (var item in definition.Ports.Where(m => edits.Contains(m.Id)))
            {
                var n = nList.First(m => m.Id == item.Id);
                item.Set(n.ParentId, n.X, n.Y, n.Width, n.Height, n.PortAlignments);
            }
            //添加
            foreach (var n in nList.Where(m => adds.Contains(m.Id)))
            {
                var p = new Port();
                p.SetId(n.Id);
                p.Set(n.ParentId, n.X, n.Y, n.Width, n.Height, n.PortAlignments);
                definition.Ports.Add(p);
                // definition.Nodes!.Find(m=>m.Id==n.ParentId)!.Ports.Add(p);

            }
        }
        private void NodeSave(DefinitionSaveDto input, Definition definition)
        {
            var dbids = definition.Nodes.Select(m => m.Id).ToList();
            var nList = input.Nodes;
            var nids = nList.Select(m => m.Id).ToList();

            var dels = dbids.Except(nids).ToList();
            var adds = nids.Except(dbids).ToList();
            var edits = dbids.Intersect(nids).ToList();
            //删除
            definition.Nodes.Where(m => dels.Contains(m.Id)).ToList().ForEach(m => m.SoftDelete());
            //更新
            foreach (var node in definition.Nodes.Where(m => edits.Contains(m.Id)))
            {
                var n = nList.First(m => m.Id == node.Id);
                node.Set(n.Title, n.X, n.Y, n.Width, n.Height);
                node.SetExtraProperties(n.ExtraProperties);
            }
            //添加
            foreach (var n in nList.Where(m => adds.Contains(m.Id)))
            {
                var node = new Node(n.Id);
                node.Set(n.Title, n.X, n.Y, n.Width, n.Height);
                node.SetExtraProperties(n.ExtraProperties);
                definition.Nodes.Add(node);
            }
        }

        public async Task<DefinitionSaveDto> FindDetailAsync(Guid id)
        {
            var entity = await _definitionManager.FindAsync(id, true);
            var rs = ObjectMapper.Map<Definition, DefinitionSaveDto>(entity);
            return rs;
        }
        public async Task<DefinitionDto> UpdateAsync(Guid id, DefinitionUpdateDto input)
        {
            var entity = await _definitionRepository.FindAsync(id);
            entity.Set(input.Name, input.Version);
            return ObjectMapper.Map<Definition, DefinitionDto>(entity);
        }
        public async Task<bool> IsExistAsync(Guid id)
        {
            var query = await _definitionRepository.GetQueryableAsync();
            var count = query.Count(m => m.Id == id);
            return count > 0;
        }

    }
}